//Dom 10 Fev 2008 10:15:18 BRT 

#include <system/kernel.h>
#include <types.h>

extern	void	idt_load();
extern	void	isr0();
extern	void	isr1();
extern	void	isr14();

extern	void	irq0();
extern	void	irq1();

static	void*	irq_handle[16] =
	{
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
	};

struct idt_entry
{
	ushort_t	offset_low;
	ushort_t	selector;
	ushort_t	attributes;
	ushort_t	offset_high;
} __attribute__((packed));

struct	idt_t
{
	ushort_t	limit;
	ulong_t		ptr;
} __attribute__((packed));

idt_entry_t	idttbl[0xFF];
struct	idt_t	idtp;

void	idt_set_isr(uchar_t n, ushort_t sel, dpl_t dpl, ulong_t offset)
{
	idttbl[n].offset_low = offset;
	idttbl[n].offset_high = offset >> 16;
	idttbl[n].selector = sel;
	idttbl[n].attributes = 0x8E00 | (dpl << 12);
}

void	isr_divide_error(regs_t r)
{
	kprintf("\nA Divide by Zero exception has been ocurred!\n");
	kprintf("CS = 0x%x\tEIP = 0x%x\n\n", r.cs, r.eip);
	kbasic_skip_main(-1);
}

void	isr_ex_dep()
{
	
}

void	isr_page_fault(ulong_t cr3, regs_t r)
{
	kprintf("\nA Page Fault exception has been occured!\nCR3 = 0x%x\n", cr3);
	kbasic_skip_main(0);
}

void	remap_8259()
{
	out(0x20, 0x11); /* ICW1 */
	out(0xA0, 0x11);

	out(0x21, 0x20); /* ICW2: roteia IRQs 0...7 para INTs 20h...27h */
	out(0xA1, 0x28); /* ...IRQs 8...15 para INTs 28h...2Fh */

	out(0x21, 0x04); /* ICW3 */
	out(0xA1, 0x02);

	out(0x21, 0x01); /* ICW4 */
	out(0xA1, 0x01);
}

void	kbasic_idt_setup()
{
	int_t	i;
	bzero(&idttbl, (sizeof(idt_entry_t) * 256));
	disable();
	remap_8259();
	idt_set_isr(0, KCODE32, RING0, (ulong_t)isr0);
	idt_set_isr(1, KCODE32, RING0, (ulong_t)isr1);
	idt_set_isr(14, KCODE32, RING0, (ulong_t)isr14);

	for(i = 32; i < 48; i++)
		idt_set_isr(i, KCODE32, RING0, (ulong_t)NULL);

	idtp.ptr = (ulong_t)virtual2physical(&idttbl);
	idtp.limit = (sizeof(idt_entry_t) * 256) - 1;
	idt_load();
	enable();
}

void	kbasic_irq_setup(uchar_t irq, void* callback)
{
	if(irq > 15 || irq < 0 || irq == 2)
		return;
	irq_handle[irq] = callback;
	void	(*irq_n)();
	switch(irq)
	{
		case 0:
			irq_n = irq0;
			break;
		case 1:
			irq_n = irq1;
			break;
		default:
			irq_n = NULL;
			break;
	}
	idt_set_isr(32 + irq , KCODE32, RING0, (ulong_t)irq_n);
}

void	irq_common_callback(regs_t* r)
{
	void (*current_irq)(regs_t* r);
	current_irq = irq_handle[ r->int_no - 32 ];
	if(current_irq)
		current_irq(r);

	if (r->int_no >= 40)
	{
		out(0xA0, 0x20);
	}
	out(0x20, 0x20);
}

